#include "config.h"

#include <math.h>

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

#include <clx/libECM.h>

#include "libms2sim.h"

#ifdef _WIN32
static int rint(double x)
{
	if (x > 0)
		return (int)(x + 0.5);
	else
		return (int)(x);
}
#endif

typedef struct {
	msl_sesslen_type_t	type;
	ecm_double	alpha, beta;
	gsl_rng	*rng;
} sesslen_t;

msl_sesslen_t
msl_sesslen_create(msl_sesslen_type_t type, unsigned long seed)
{
	sesslen_t	*sesslen;
	gsl_rng	*rng;

	rng = gsl_rng_alloc(gsl_rng_default);
	if (rng == NULL)
		return NULL;
	sesslen = (sesslen_t *)ecm_malloc(sizeof(sesslen_t));
	if (sesslen == NULL) {
		gsl_rng_free(rng);
		return NULL;
	}
	if (seed > 0)
		gsl_rng_set(rng, seed);

	sesslen->rng = rng;
	sesslen->type = type;
	sesslen->alpha = 0.5;
	sesslen->beta = 0.5;

	return sesslen;
}

static unsigned int
get_with_beta(sesslen_t *sesslen, unsigned long media_length)
{
	double	ratio;
	unsigned int	session_length;

	ratio = gsl_ran_beta(sesslen->rng, sesslen->alpha, sesslen->beta);
	session_length = (unsigned int)(rint(ratio * media_length) + 1);
	if (session_length > media_length)
		session_length = media_length;
	return session_length;
}

int
msl_sesslen_set_param(msl_sesslen_t msl_sesslen, ecm_double alpha, ecm_double beta)
{
	sesslen_t	*sesslen = (sesslen_t *)msl_sesslen;

	sesslen->alpha = alpha;
	sesslen->beta = beta;

	return 0;
}

unsigned int
msl_sesslen_get(msl_sesslen_t msl_sesslen, unsigned long media_length)
{
	sesslen_t	*sesslen = (sesslen_t *)msl_sesslen;
	unsigned int	session_length;

	if (sesslen == NULL)
		return 0;

	switch (sesslen->type) {
	case MSL_SESSLEN_TYPE_BETA:
		session_length = get_with_beta(sesslen, media_length);
		break;
	default:
		return 0;
	}
	return session_length;
}

void
msl_sesslen_free(msl_sesslen_t msl_sesslen)
{
	sesslen_t	*sesslen = (sesslen_t *)msl_sesslen;

	if (sesslen == NULL)
		return;

	gsl_rng_free(sesslen->rng);
	ecm_free(sesslen);
}

msl_sesslen_type_t
msl_sesslen_parse_typestr(const char *typestr)
{
	if (ecm_strcmp(typestr, "full") == 0)
		return MSL_SESSLEN_TYPE_FULL;
	if (ecm_strcmp(typestr, "beta") == 0)
		return MSL_SESSLEN_TYPE_BETA;
	return MSL_SESSLEN_TYPE_NONE;
}

const char *
msl_sesslen_get_typestr(msl_sesslen_type_t type)
{
	switch (type) {
	case MSL_SESSLEN_TYPE_FULL:
		return "full";
	case MSL_SESSLEN_TYPE_BETA:
		return "beta";
	default:
		return "none";
	}
}
